import java.nio.*;
import java.util.*;

/**
 *
 * @author Russell
 */
public class RsaEncryption {

    private int p;
    private int q;
    private int d;
    private int e;
    private int n;
    private int[] cipherText;
    private int[] decryptedText;
    private byte[] plainText;
    private int plainTextLength;
    private int plain;
    private String binString;
    private String binStringD;
    private String cipherString;
    private byte[] cipherArray;
	
	public RsaEncryption(byte[] input, byte[] byteKey, byte[] byteN) {
		//convert to int and set e & n
		this.e = ByteBuffer.wrap(byteKey).getInt();
		this.n = ByteBuffer.wrap(byteN).getInt();
		
        this.plainText = input;   
        this.plainTextLength = this.plainText.length;   // number of bytes to be encrypted
        this.cipherArray = new byte[this.plainText.length];
        this.cipherText = new int[this.plainTextLength];
        this.decryptedText = new int[this.plainTextLength];
        this.cipherString = "";

        RsaAlgorithm();
	}
	
    public RsaEncryption(String name, boolean encrypt, byte[] plainText) {
        if (name == "Bob") {
            this.p = 11;
            this.q = 23;
            if (encrypt) {
                this.e = 147;
            } else {
                this.e = 3; // public key
            }
            this.n = this.p * this.q;   // n = pq
        } else {
            this.p = 13;
            this.q = 19;
            if (encrypt) {
                this.e = 173;
            } else {
                this.e = 5; // public key
            }
            this.n = this.p * this.q;   // n = pq
        }

        //this.plainText = new byte[plainText.length];
        this.plainText = plainText;    // plaintext that was input
        this.plainTextLength = this.plainText.length;   // number of bytes to be encrypted
        this.cipherArray = new byte[this.plainText.length];
        this.cipherText = new int[this.plainTextLength];
        this.decryptedText = new int[this.plainTextLength];
        this.cipherString = "";

        /*
        if (this.e == 5) {
        this.d = 173; // private key
        this.binStringD = Integer.toBinaryString(this.d);
        }
         * 
         */
        RsaAlgorithm();
    }

    public void RsaAlgorithm() {
        //c= m^e mod n; c = plaintext^e mod n;
        int exp = this.e;
        this.binString = Integer.toBinaryString(this.e); // binary format of e
        int length = this.binString.length();   // length of e's binstring

        //System.out.print(" Input per RSA using ASCII int value is: ");
        for (int j = 0; j < this.plainTextLength; j++) {      // loop through each byte individually to encrypt
            this.plain = this.plainText[j] & 0xFF;   // cast as int per RSA method
            // System.out.print(this.plain + ",");
            int counter = 0;
            int temp = this.plain * this.plain;   // do first square outside of loop
            int last = temp % this.n;     // find first mod outside of loop
            if (this.binString.charAt(1) == '1') {
                temp = last * this.plain;
                last = temp % this.n;
            }

            //    System.out.println("round " + j + " n = " + this.n + " squared = " + temp + " mod n = " + last);  // print result for troubleshooting
            int type;
            for (int i = 2; i < length; i++) {         // using binary string, use shift operations and if next digit is a 1, then last*number
                if (this.binString.charAt(i) == '1') {
                    temp = last * last;
                    last = temp % this.n;
                    temp = last * this.plain;
                    last = temp % this.n;
                    type = 1;
                } else {   // else, last^2
                    temp = last * last;
                    last = temp % this.n;
                    type = 0;
                }
                //   System.out.println(" squared = " + temp + " mod n = " + last + " type = " + type);
            }
            this.cipherText[j] = last;
            this.cipherArray[j] = (byte) last;
            this.setCipherString(this.getCipherString() + (char) last);
        }
        //System.out.print("\n Ciphertext as ASCII int value: ");
        //for (int i = 0; i < this.cipherText.length; i++) {
        //    System.out.print(this.cipherText[i] + ",");
        //}
        //System.out.println(this.getCipherString());
    }

    /**
     * @return the cipherString
     */
    public String getCipherString() {
        return cipherString;
    }

    /**
     * @param cipherString the cipherString to set
     */
    public void setCipherString(String cipherString) {
        this.cipherString = cipherString;
    }

    /**
     * @return the cipherArray
     */
    public byte[] getCipherArray() {
        return cipherArray;
    }
}
